package yz.com.javautil.date;

import org.apache.commons.lang3.time.DateFormatUtils;
import org.joda.time.DateTime;
import org.joda.time.LocalDate;

import java.lang.management.ManagementFactory;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;

/**
 * 时间工具类
 *
 * @author xinan
 */
public class DateUtils extends org.apache.commons.lang3.time.DateUtils {

    public static String YYYY = "yyyy";

    public static String YYYY_MM = "yyyy-MM";

    public static String YYYY_MM_DD = "yyyy-MM-dd";

    public static String yyyyMMdd = "yyyyMMdd";

    public static String YYYYMM = "yyyyMM";

    public static String YYYYMMDDHHMMSS = "yyyyMMddHHmmss";

    public static String YYYYMMDDHHMMSSSSS = "yyyyMMddHHmmssSSS";

    public static String YYYY_MM_DD_HH_MM_SS = "yyyy-MM-dd HH:mm:ss";
    public static String MM_DD_HH_MM_SS = "MM-dd HH:mm:ss";
    public static String MM_DD = "MM-dd";
    public static String HH_MM_SS = "HH:mm:ss";
    private static String[] parsePatterns = {
            "yyyy-MM-dd", "yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd HH:mm", "yyyy-MM",
            "yyyy/MM/dd", "yyyy/MM/dd HH:mm:ss", "yyyy/MM/dd HH:mm", "yyyy/MM",
            "yyyy.MM.dd", "yyyy.MM.dd HH:mm:ss", "yyyy.MM.dd HH:mm", "YYYYMMDD",
            "yyyy-MM-dd HH:mm:ss.SSS", "yyyy-MM-dd'T'HH:mm:ss", "yyyy-MM-dd'T'HH:mm"
            , "yyyy-MM-dd HH时", "yyyy年MM月dd日 HH时mm分ss秒", "yyyy年MM月dd日"};

    /**
     * 获取当前Date型日期
     *
     * @return Date() 当前日期
     */
    public static Date getNowDate() {
        return new Date();
    }

    /**
     * 获取当前日期, 默认格式为yyyy-MM-dd
     *
     * @return String
     */
    public static String getDate() {
        return dateTimeNow(YYYY_MM_DD);
    }

    public static final String getTime() {
        return dateTimeNow(YYYY_MM_DD_HH_MM_SS);
    }

    public static final String dateTimeNow() {
        return dateTimeNow(YYYYMMDDHHMMSS);
    }

    public static final String getYyyyMmDd() {
        return dateTimeNow(yyyyMMdd);
    }

    public static final String getYyyyMm() {
        return dateTimeNow(YYYYMM);
    }

    public static final String getDateHasSss() {
        return dateTimeNow(YYYYMMDDHHMMSSSSS);
    }

    public static final String dateTimeNow(final String format) {
        return parseDateToStr(format, new Date());
    }

    public static final String dateTime(final Date date) {
        return parseDateToStr(YYYY_MM_DD, date);
    }

    public static final String parseDateToStr(final String format, final Date date) {
        return new SimpleDateFormat(format).format(date);
    }

    public static final Date dateTime(final String format, final String ts) {
        try {
            return new SimpleDateFormat(format).parse(ts);
        } catch (ParseException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 日期路径 即年/月/日 如2018/08/08
     */
    public static final String datePath() {
        Date now = new Date();
        return DateFormatUtils.format(now, "yyyy/MM/dd");
    }

    /**
     * 日期路径 即年/月/日 如20180808
     */
    public static final String dateTime() {
        Date now = new Date();
        return DateFormatUtils.format(now, "yyyyMMdd");
    }

    /**
     * 日期型字符串转化为日期 格式
     */
    public static Date parseDate(Object str) {
        if (str == null) {
            return null;
        }
        try {
            return parseDate(str.toString(), parsePatterns);
        } catch (ParseException e) {
            return null;
        }
    }

    /**
     * 获取服务器启动时间
     */
    public static Date getServerStartDate() {
        long time = ManagementFactory.getRuntimeMXBean().getStartTime();
        return new Date(time);
    }

    /**
     * 计算两个时间差
     */
    public static String getDatePoor(Date endDate, Date nowDate) {
        long nd = 1000 * 24 * 60 * 60;
        long nh = 1000 * 60 * 60;
        long nm = 1000 * 60;
        // long ns = 1000;
        // 获得两个时间的毫秒时间差异
        long diff = endDate.getTime() - nowDate.getTime();
        // 计算差多少天
        long day = diff / nd;
        // 计算差多少小时
        long hour = diff % nd / nh;
        // 计算差多少分钟
        long min = diff % nd % nh / nm;
        // 计算差多少秒//输出结果
        // long sec = diff % nd % nh % nm / ns;
        return day + "天" + hour + "小时" + min + "分钟";
    }

    //判断日期格式
    public static Boolean isDateVail(String date, String pattern) {
        //用于指定 日期/时间 模式
        DateTimeFormatter dtf = DateTimeFormatter.ofPattern(pattern);
        boolean flag = true;
        try {
            //Java 8 新添API 用于解析日期和时间
            LocalDateTime.parse(date, dtf);
        } catch (Exception e) {
            flag = false;
        }
        return flag;
    }

    /**
     * 计算两个日期之间的差距天数
     *
     * @param a
     * @param b
     * @return
     */
    public static int cutTwoDateToDay(Date a, Date b) {
        SimpleDateFormat format = new SimpleDateFormat(YYYY_MM_DD);
        Calendar calendar = Calendar.getInstance();
        int theday = 0;
        try {
            Date beginDate = format.parse(format.format(a));
            Date endDate = format.parse(format.format(b));

            calendar.setTime(beginDate);
            long begin = calendar.getTimeInMillis();
            calendar.setTime(endDate);
            long end = calendar.getTimeInMillis();

            theday = (int) ((end - begin) / (86400000));
        } catch (ParseException e) {
            e.printStackTrace();
        }
        return theday;
    }

    public static String getCurrentDate(String format) {
        SimpleDateFormat df = new SimpleDateFormat(format);
        return df.format(new Date());
    }

    /**
     * 将数字型的时间转为字符串（80 -> 01:20）
     *
     * @param time
     * @return
     */
    public static String intToTimeString(int time) {
        String hour = String.valueOf(time / 60);
        String minute = String.valueOf(time - time / 60 * 60);

        if (hour.length() < 2) {
            hour = "0" + hour;
        }
        if (minute.length() < 2) {
            minute = "0" + minute;
        }
        return hour + ":" + minute;
    }

    /**
     * 取出两个时间出较大的时间
     *
     * @param a
     * @param b
     * @return
     */
    public static Date MaxDate(Date a, Date b) {
        if (a.before(b)) {
            return b;
        } else {
            return a;
        }
    }

    /**
     * 取出两个时间出较小的时间
     *
     * @param a
     * @param b
     * @return
     */
    public static Date MinDate(Date a, Date b) {
        if (a.before(b)) {
            return a;
        } else {
            return b;
        }
    }

    /**
     * 计算给定日期是星期几
     *
     * @param date
     * @return
     */
    public static int getWeekOfDate(Date date) {
        Calendar cal = Calendar.getInstance();
        cal.setTime(date);
        int w = cal.get(Calendar.DAY_OF_WEEK) - 1;
        if (w == 0) {
            w = 7;
        }
        return w;
    }

    /**
     * 格式化日期
     *
     * @param date
     * @param pattern
     * @return
     */
    public static String formatDate(Date date, String pattern) {
        SimpleDateFormat format = new SimpleDateFormat(pattern);
        return format.format(date);
    }

    /**
     * 根据给定日期字符串和日期格式 创建日期
     *
     * @param dateString
     * @param pattern
     * @return
     * @throws ParseException
     */
    public static Date createDate(String dateString, String pattern) throws ParseException {
        SimpleDateFormat format = new SimpleDateFormat(pattern);
        return format.parse(dateString);
    }

    /**
     * 返回指定年数位移后的日期
     */
    public static Date yearOffset(Date date, int offset) {
        return offsetDate(date, Calendar.YEAR, offset);
    }

    /**
     * 返回指定月数位移后的日期
     */
    public static Date monthOffset(Date date, int offset) {
        return offsetDate(date, Calendar.MONTH, offset);
    }

    /**
     * 返回指定天数位移后的日期
     */
    public static Date dayOffset(Date date, int offset) {
        return offsetDate(date, Calendar.DATE, offset);
    }

    /**
     * 返回指定日期相应位移后的日期
     *
     * @param date   参考日期
     * @param field  位移单位，见 {@link Calendar}
     * @param offset 位移数量，正数表示之后的时间，负数表示之前的时间
     * @return 位移后的日期
     */
    public static Date offsetDate(Date date, int field, int offset) {
        Calendar calendar = convert(date);
        calendar.add(field, offset);
        return calendar.getTime();
    }

    private static Calendar convert(Date date) {
        Calendar calendar = new GregorianCalendar();
        calendar.setTime(date);
        return calendar;
    }

    public static Date parseDate(String value, Class targetType, String... formats) {
        for (String format : formats) {
            try {
                long v = new SimpleDateFormat(format).parse(value).getTime();
                return (Date) targetType.getConstructor(long.class).newInstance(v);
            } catch (ParseException e) {
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
            try {
                return (Date) targetType.getConstructor(String.class).newInstance(value);
            } catch (Exception e) {
            }
        }
        throw new IllegalArgumentException("cannot parse:" + value + " for date by formats:" + Arrays.asList(formats));
    }

    public static boolean isDateType(Class<?> targetType) {
        if (targetType == null) {
            return false;
        }
        return targetType == Date.class || targetType == java.sql.Timestamp.class || targetType == java.sql.Date.class || targetType == java.sql.Time.class;
    }

    /**
     * add By Nigel
     * 将java.util.Date
     * 转化为形如
     * to_date('2012-12-12 13:12:12','yyyy-MM-dd HH:mm:ss')
     * 的字符串
     *
     * @param date
     * @return
     */
    public static String javaDateToOracleDateSQL(Date date) {
        String dateStr = (new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")).format(date);
        return "to_date('" + dateStr + "','yyyy-MM-dd hh24:mi:ss')";
    }

    public static String getTimeStr(String fomatStr) {
        DateFormat dateFormat = new SimpleDateFormat(fomatStr);
        String currernTime = dateFormat.format(new Date());
        return currernTime;
    }

    //返回位移指定时间后的时间
    public static String offsetByType(String time, int diff, int type) {
        SimpleDateFormat df = new SimpleDateFormat(YYYY_MM_DD_HH_MM_SS);
        Calendar calendar = Calendar.getInstance();
        try {
            calendar.setTime(df.parse(time));
            calendar.add(type, diff);//这里按时间类型进行推移先用的时间
            df.parse(time);
        } catch (ParseException e) {
            e.printStackTrace();
        }
        return df.format(calendar.getTime());
    }

    /**
     * 时间移动
     *
     * @param time      时间字符串
     * @param diff      移动数量，正数是向后推时间 负数时向前推
     * @param type      移动类型
     * @param formatStr
     * @return
     */
    public static String offsetByType(String time, int diff, String type, String formatStr) {
        SimpleDateFormat df = new SimpleDateFormat(formatStr);
        Date date = null;
        try {
            date = offsetByType(df.parse(time), diff, type, formatStr);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return df.format(date);
    }

    public static Date offsetByType(Date time, int diff, String type, String formatStr) {
        int typeI = Calendar.HOUR;
        if ("second".equalsIgnoreCase(type)) {
            typeI = Calendar.SECOND;
        } else if ("minut".equalsIgnoreCase(type)) {
            typeI = Calendar.MINUTE;
        } else if ("hour".equalsIgnoreCase(type)) {
            typeI = Calendar.HOUR;
        }
        SimpleDateFormat df = new SimpleDateFormat(formatStr);
        Calendar calendar = Calendar.getInstance();
        try {
            calendar.setTime(time);
            calendar.add(typeI, diff);//这里按时间类型进行推移先用的时间
        } catch (Exception e) {
            e.printStackTrace();
        }
        return calendar.getTime();
    }


    /**
     * 获取起始时间-截止时间中的日期集合
     *
     * @param dBegin
     * @param dEnd
     * @return
     */
    public static List<Date> findDates(Date dBegin, Date dEnd) {
        ArrayList<Date> lDate = new ArrayList<Date>(1);
        lDate.add(dBegin);
        Calendar calBegin = Calendar.getInstance();
        // 使用给定的 Date 设置此 Calendar 的时间
        calBegin.setTime(dBegin);
        Calendar calEnd = Calendar.getInstance();
        // 使用给定的 Date 设置此 Calendar 的时间
        calEnd.setTime(dEnd);
        // 测试此日期是否在指定日期之后
        while (dEnd.after(calBegin.getTime())) {
            // 根据日历的规则，为给定的日历字段添加或减去指定的时间量
            calBegin.add(Calendar.DAY_OF_MONTH, 1);
            lDate.add(calBegin.getTime());
        }
        return lDate;
    }

    /**
     * 获取当前月的最后一天 返回格式 YYYY-MM-DD
     *
     * @param date
     * @return
     */
    public static String getMothLastDay(Date date) {
        SimpleDateFormat format = new SimpleDateFormat(YYYY_MM_DD);
        //获取当前月最后一天
        Calendar ca = Calendar.getInstance();
        ca.setTime(date);
        ca.set(Calendar.DAY_OF_MONTH, ca.getActualMaximum(Calendar.DAY_OF_MONTH));
        String lastDay = format.format(ca.getTime());
        return lastDay;
    }

    /**
     * 根据日期获取最小时段及最大时段日期
     *
     * @param date 2021-11-01
     * @param type MIN MAX
     * @return 2021-11-01 00:00:00 或 2021-11-01 23:59:59
     */
    public static String getStrDateByType(Date date, String pattern, String type) {
        String typeMin = "MIN", typeMax = "MAX";
        SimpleDateFormat sdf = new SimpleDateFormat(pattern);
        String dateStr = sdf.format(date);
        if (typeMin.equals(type)) {
            dateStr += " 00:00:00";
        } else if (typeMax.equals(type)) {
            dateStr += " 23:59:59";
        }
        return dateStr;
    }

    /**
     * 按天改变日期值
     *
     * @param date    要修改的日期
     * @param daysNum 天数（正值为增加 负值为减少）
     * @return date
     */
    public static Date getDateByDay(Date date, int daysNum) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        calendar.add(Calendar.DATE, daysNum);
        return calendar.getTime();
    }

    /**
     * 获取每周的开始日期、结束日期
     *
     * @param week 周期  0本周，-1上周，-2上上周，1下周，2下下周；依次类推
     * @return 返回date[0]开始日期、date[1]结束日期
     */
    public static Date[] getBeginAndEndOfTheWeek(int week) {
        DateTime dateTime = new DateTime();
        LocalDate date = new LocalDate(dateTime.plusWeeks(week));

        date = date.dayOfWeek().withMinimumValue();
        Date beginDate = date.toDate();
        Date endDate = date.plusDays(6).toDate();
        return new Date[]{beginDate, endDate};
    }

    private static String formatDateByExpression(String dateStr, String expression) {
        SimpleDateFormat simpleDateParse = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat(expression);
        Date parse;
        String format = null;
        try {
            parse = simpleDateParse.parse(dateStr);
            format = simpleDateFormat.format(parse);
        } catch (ParseException e) {
        }
        return format;
    }

    public static void main(String[] args) {
        String s = formatDateByExpression("2022-06-23 13:59:32.157907+08", "yyyy-MM-dd HH:mm");
        System.out.println(s);

    }


}
